﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Web.Script.Serialization;
using System.Security.Principal;

using MASAS.MSM.SharePoint.REST.Config;
using MASAS.MSM.DomainLayer.Logging;
using MASAS.MSM.SharePoint.REST.OAuth;

namespace MASAS.MSM.SharePoint.REST.Utils
{

    /// <summary>
    /// Class SPHelper_REST. This class cannot be inherited.
    /// </summary>
    internal sealed class SPHelper_REST
    {

        /// <summary>
        /// Gets the access token.
        /// </summary>
        /// <param name="scData">The site configuration data.</param>
        /// <returns>System.String.</returns>
        private static string GetAccessToken( SiteConfigData scData )
        {
            if( string.IsNullOrEmpty( scData.AccessToken ) )
            {
                scData.AccessToken = TokenHelper.GetS2SAccessTokenWithWindowsIdentity( new Uri( scData.Url ), WindowsIdentity.GetCurrent() );
            }

            return scData.AccessToken;
        }

        /// <summary>
        /// Applies the credentials.
        /// </summary>
        /// <param name="endpointRequest">The endpoint request.</param>
        /// <param name="scData">The site configuration data.</param>
        private static void ApplyCredentials( HttpWebRequest endpointRequest, SiteConfigData scData )
        {
            switch( scData.AccessType )
            {
                case SiteConfigData.accessTypeEnum.defaultAccess:
                    endpointRequest.Credentials = System.Net.CredentialCache.DefaultCredentials;
                    break;
                case SiteConfigData.accessTypeEnum.credentialAccess:
                    endpointRequest.Credentials = new System.Net.NetworkCredential( scData.UserName, scData.UserPassword, scData.UserDomain );
                    break;
                case SiteConfigData.accessTypeEnum.tokenAccess:
                    string accessToken = GetAccessToken( scData );
                    endpointRequest.Headers.Add( "Authorization", "Bearer " + scData.AccessToken );
                    break;
            }
        }

        /// <summary>
        /// Requests data from a REST endpoint.
        /// </summary>
        /// <param name="requestUrl">The endpoint URL.</param>
        /// <param name="scData">The site configuration data.</param>
        /// <returns>System.String.</returns>
        static internal string RequestData( string requestUrl, SiteConfigData scData )
        {
            String responseStr = "";

            HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create( requestUrl );
            endpointRequest.Method = "GET";
            endpointRequest.Accept = "application/json;odata=verbose";

            // Apply the credentials...
            ApplyCredentials( endpointRequest, scData );
            

            try
            {
                WebResponse response = endpointRequest.GetResponse();
                using( Stream responseStream = response.GetResponseStream() )
                {
                    StreamReader reader = new StreamReader( responseStream, Encoding.UTF8 );
                    responseStr = reader.ReadToEnd();

                    reader.Close();
                }
                response.Close();
            }
            catch( WebException ex )
            {
                WebResponse errorResponse = ex.Response;

                HttpWebResponse webResponse = (HttpWebResponse)errorResponse;
                if( webResponse.StatusCode != HttpStatusCode.NotFound )
                {
                    using( Stream responseStream = errorResponse.GetResponseStream() )
                    {
                        StreamReader reader = new StreamReader( responseStream, Encoding.GetEncoding( "utf-8" ) );
                        String errorText = reader.ReadToEnd();

                        Logger.AddLogEntry( "An error occurred: " + errorText, ex );

                        reader.Close();
                    }
                }
            }

            return responseStr;
        }

        /// <summary>
        /// Posts data to a REST endpoint.
        /// </summary>
        /// <param name="postUrl">The endpoint URL.</param>
        /// <param name="postData">The data to be posted.</param>
        /// <param name="scData">The site configuration data.</param>
        /// <param name="formDigest">The form digest.</param>
        /// <returns>System.String.</returns>
        static internal string PostData( string postUrl, string postData, SiteConfigData scData, string formDigest = null )
        {
            String responseStr = "";
            byte[] postDataBytes = Encoding.UTF8.GetBytes( postData );

            HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create( postUrl );
            endpointRequest.Method = "POST";
            endpointRequest.Accept = "application/json;odata=verbose";
            endpointRequest.ContentType = "application/json;odata=verbose";
            endpointRequest.ContentLength = postDataBytes.Length;

            if( !string.IsNullOrEmpty( formDigest ) )
            {
                endpointRequest.Headers["X-RequestDigest"] = formDigest;
            }

            // Apply the credentials...
            ApplyCredentials( endpointRequest, scData );

            Stream dataStream = endpointRequest.GetRequestStream();
            dataStream.Write( postDataBytes, 0, postDataBytes.Length );
            dataStream.Close();

            try
            {
                WebResponse response = endpointRequest.GetResponse();

                using( Stream responseStream = response.GetResponseStream() )
                {
                    StreamReader reader = new StreamReader( responseStream, Encoding.UTF8 );
                    responseStr = reader.ReadToEnd();

                    reader.Close();
                }
                response.Close();

                HttpWebResponse webResponse = (HttpWebResponse)response;
                if( !( webResponse.StatusCode == HttpStatusCode.Created || webResponse.StatusCode == HttpStatusCode.OK ) )
                {
                    Logger.AddLogEntry( "An error occurred: " + responseStr, LogEventType.Error );
                }
                
            }
            catch( WebException ex )
            {
                WebResponse errorResponse = ex.Response;
                using( Stream responseStream = errorResponse.GetResponseStream() )
                {
                    StreamReader reader = new StreamReader( responseStream, Encoding.GetEncoding( "utf-8" ) );
                    responseStr = reader.ReadToEnd();

                    Logger.AddLogEntry( "An error occurred: " + responseStr, ex );

                    reader.Close();
                }
            }

            return responseStr;
        }

        /// <summary>
        /// Posts a data update to a REST endpoint.
        /// </summary>
        /// <param name="postUrl">The endpoint URL.</param>
        /// <param name="postData">The data to be posted.</param>
        /// <param name="scData">The site configuration data.</param>
        /// <param name="formDigest">The form digest.</param>
        /// <returns><c>true</c> if the update was successful, <c>false</c> otherwise.</returns>
        static internal bool UpdateData( string postUrl, string postData, SiteConfigData scData, string formDigest = null )
        {
            bool retValue = false;
            String responseStr = "";
            byte[] postDataBytes = Encoding.UTF8.GetBytes( postData );

            HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create( postUrl );
            endpointRequest.Method = "POST";
            endpointRequest.Accept = "application/json;odata=verbose";
            endpointRequest.ContentType = "application/json;odata=verbose";
            endpointRequest.ContentLength = postDataBytes.Length;

            if( !string.IsNullOrEmpty( formDigest ) )
            {
                endpointRequest.Headers["X-RequestDigest"] = formDigest;
            }

            endpointRequest.Headers["X-HTTP-Method"] = "MERGE";
            endpointRequest.Headers["IF-MATCH"] = "*";

            // Apply the credentials...
            ApplyCredentials( endpointRequest, scData );

            Stream dataStream = endpointRequest.GetRequestStream();
            dataStream.Write( postDataBytes, 0, postDataBytes.Length );
            dataStream.Close();

            try
            {
                WebResponse response = endpointRequest.GetResponse();
                HttpWebResponse webResponse = (HttpWebResponse)response;
                if( webResponse.StatusCode == HttpStatusCode.NoContent )
                {
                    retValue = true;
                }
                else
                {
                    using( Stream responseStream = response.GetResponseStream() )
                    {

                        StreamReader reader = new StreamReader( responseStream, Encoding.UTF8 );
                        responseStr = reader.ReadToEnd();

                        reader.Close();
                    }

                    Logger.AddLogEntry( "An error occurred: " + responseStr, LogEventType.Error );
                }
                response.Close();

            }
            catch( WebException ex )
            {
                WebResponse errorResponse = ex.Response;
                using( Stream responseStream = errorResponse.GetResponseStream() )
                {
                    StreamReader reader = new StreamReader( responseStream, Encoding.GetEncoding( "utf-8" ) );
                    responseStr = reader.ReadToEnd();

                    Logger.AddLogEntry( "An error occurred: " + responseStr, ex );

                    reader.Close();
                }
            }

            return retValue;
        }

    }

}
